/**
 * Copyright (c) 2020 Tenny
 *  typescript-init is licensed under Mulan PSL v2.
 *  You can use this software according to the terms and conditions of the Mulan PSL v2.
 *  You may obtain a copy of Mulan PSL v2 at:
 *           http://license.coscl.org.cn/MulanPSL2
 *  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *  See the Mulan PSL v2 for more details.
 */

const utils = require('../utils');

// 命令行颜色值
const csColor = {
  error: '\x1B[31m%s\x1B[0m'
};

/**
 * 命令行工具类
 */
class Commander {
  constructor () {
    this._name = ""; // 名称
    this._version = "0.0.1"; // 版本
    this._command = null; // 保存命令
    this._options = new Map([
      ['help', new Map([
        ['description', '展示命令的帮助信息'],
        ['alias', 'h'],
        ['flag', 2]
      ])],
      ['version', new Map([
        ['description', '输出版本号'],
        ['alias', 'V'],
        ['flag', 2]
      ])]
    ]);
    this._optionsAlias = new Map([
      ['h', 'help'],
      ['V', 'version']
    ]); // 帮助指令的别名
    this._optValues = {};
  }

  /**
   * 注册项目名称
   * @param {String} name 项目名称
   */
  name (name) {
    this._name = name;
    return this;
  }

  /**
   * 注册命令
   * @param {String} command      注册命令
   * @param {String} alias        别名
   * @param {String} description  描述
   */
  command (command, alias, description) {
    if (this._command == null) {
      command = command.split(' ');
      let cmd = [];
      if (utils.isBlank(command[0])) {
        throw new Error('command name error!');
      }
      let cn = [command[0].trim()];
      if (!utils.isBlank(alias)) {
        cn.push(alias.trim());
      }
      cmd.push(['command', cn]);
      cmd.push(['param', !utils.isBlank(command[1]) ? command[1].trim().match(/<(.+)>/)[1] : '']);
      if (!utils.isBlank(description)) {
        cmd.push(['description', description]);
      }
      this._command = new Map(cmd)
    }
    return this;
  }

  /**
   * 解析配置选项
   * @param {String} option       选项, -d, --dir [dir]
   * @param {String} description  选项描述
   * @param {Object} deft         选项的默认值
   */
  option (option, description, deft) {
    let opt = option.split(' ');
    let n = opt.slice(0, 2);
    let name, alias;
    n.forEach(ni => {
      ni = ni.trim();
      if (ni.endsWith(',')) {
        ni = ni.substring(0, ni.length - 1);
      }
      let nm = ni.trim().match(/(-*)(.+)(,*)/);
      if (nm[1].length === 1) {
        alias = nm[2];
      } else if (nm[1].length === 2) {
        name = nm[2];
      }
    });
    if (utils.isBlank(name) && utils.isBlank(alias)) {
      throw new Error('无法识别的option: ' + option);
    }
    let om = new Map();
    om.set('description', description);
    if (!utils.isBlank(deft)) {
      om.set('default', deft);
      this._optValues[name || alias] = deft; 
    }
    if (!utils.isBlank(alias)) {
      om.set('alias', alias);
    }
    if (!(utils.isBlank(name) && !(utils.isBlank(alias)))) {
      om.set('flag', 2);
      this._optionsAlias.set(alias, name);
    } else if (utils.isBlank(name)) {
      om.set('flag', 0);
    } else {
      om.set('flag', 1);
    }
    if (!utils.isBlank(opt[1])) {
      let key = opt[2].trim().match(/\[(.+)\]/);
      if (key) {
        key = key[1];
      }
      if (!utils.isBlank(key)) {
        om.set('key', key);
      }
    }
    this._options.set(name || alias, om);
    return this;
  }

  /**
   * 注册版本号
   * @param {String} version 版本号
   */
  version (version) {
    this._version = version;
    return this;
  }

  /**
   * 打印帮助文档
   */
  _printHelp () {
    let helpArr = [];
    let fwidth = 0;
    for (let [key, value] of this._options) {
      let s1 = '';
      if (value.get('flag') === 0) {
        s1 = '-' + key;
      } else if (value.get('flag') === 1) {
        s1 = '--' + key;
      } else {
        s1 = '-' + value.get('alias') + ', --' + key;
      }
      if (value.has('key')) {
        s1 += ' [' + value.get('key') + ']'
      }
      let len = s1.length;
      if (len > fwidth) {
        fwidth = len;
      }
      let s2 = '';
      s2 = value.get('description') || '';
      if (value.has('default')) {
        let d = value.get('default');
        if (typeof d === 'string') {
          s2 += ' (default: "' + d + '")';
        } else {
          s2 += ' (default: ' + d + ')';
        }
      }
      helpArr.push([s1, s2]);
    }
    let printstr = [
      '使用：' + this._name + ' ' + this._command.get('command').join('|') + ' <command> [options]\r\n',
      this._command.get('description'),
      '\r\nOptions：'
    ];
    for (let i = helpArr.length - 1; i >= 0; i--) {
      let hi = helpArr[i];
      printstr.push('  ' + hi[0].padEnd(fwidth) + '  ' + hi[1])
    }
    console.log(printstr.join('\r\n'));
  }

  /**
   * 解析参数并执行解析结果
   */
  parse (cb) {
    let argv = process.argv;
    let cname = argv[3];
    // 解析命令
    let cmd = argv[2];
    if (this._command.get('command').includes(cmd)) {
      // 解析命令参数
      let name = argv[3];
      let namr = name.match(/(-*)(.+)/);
      let f = false;
      if (namr[1].length === 1) {
        f = this._optionsAlias.has(namr[2]);
      } else if (namr[1].length === 2) {
        f = this._options.has(namr[2]);
      }
      if (f === true) {
        if (namr[2] === 'V' || namr[2] === 'version') { // 打印版本信息
          console.log(this._version);
          return;
        } else if (namr[2] === 'h' || namr[2] === 'help') { // 打印帮助文档
          this._printHelp(); // 打印帮助文档
          return;
        } else {
          console.error(csColor.error, '错误：缺少命令参数 "' + this._command.get('param') + '');
          return;
        }
      }
    } else {
      console.error(csColor.error, '错误的命令：' + cmd);
      return;
    }
    argv = argv.slice(4);
    let len = argv.length;
    let i = 0;
    let errType = 0, errKey;
    while (i >= 0 && i < len) {
      let argi = argv[i];
      errKey = argi;
      let argii = argi.split('=');
      let name = argii[0].match(/(-+)(.+)/);
      let n;
      if (name == null) {
        errType = 3;
        break;
      }
      let cmd;
      if (name[1].length === 1) {
        if (this._optionsAlias.has(name[2])) {
          if (name[2] === 'V') {
            errType = 1;
            break;
          }
          if (name[2] === 'h') {
            errType = 2;
            break;
          }
          n = this._optionsAlias.get(name[2]);
          cmd = this._options.get(n);
        } else if (this._options.has(name[2])) {
          if (name[2] === 'version') {
            errType = 1;
            break;
          }
          if (name[2] === 'help') {
            errType = 2;
            break;
          }
          n = name[2];
          cmd = this._options.get(n);
        } else {
          errType = 3;
          break;
        }
      } else if (name[1].length === 2) {
        if (this._options.has(name[2])) {
          if (name[2] === 'version') {
            errType = 1;
            break;
          }
          if (name[2] === 'help') {
            errType = 2;
            break;
          }
          n = name[2];
          cmd = this._options.get(n);
        }
      } else {
        errType = 3;
        break;
      }
      let value = argii[1];
      argii = argv[i + 1];
      if (!utils.isBlank(argii)) {
        argii = argii.match(/(-+)(.+)/);
        if (argii == null) {
          value = argv[i + 1];
          i = i + 2;
        } else {
          i++;
        }
      } else {
        i++;
      }
      this._optValues[n] = cmd.has('key') ? (value || false) : true;
    }
    if (errType === 1) { // 版本号
      console.log(this._version);
    } else if (errType === 2) { // 帮助文档
      this._printHelp();
    } else if (errType === 3) {
      console.error(csColor.error, '错误：无法识别的配置选项：' + errKey);
    } else {
      if (typeof cb === 'function') {
        cb(cname, this._optValues);
      }
    }
  }
}

module.exports = new Commander();